package com.ctsi.canal.starter.util;

import com.ctsi.canal.starter.handler.EntryHandler;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author ajh
 * @date 2019/10/24 15:24
 */
public class GenericUtil {

    private static Map<Class<? extends EntryHandler>, Class> cache = new ConcurrentHashMap<>();

    static String getTableGenericProperties(EntryHandler entryHandler) {

        return null;
    }

    @SuppressWarnings("unchecked")
    public static <T> Class<T> getTableClass(EntryHandler object) {
        Class<? extends EntryHandler> handlerClass = object.getClass();
        Class tableClass = cache.get(handlerClass);
        if (tableClass == null) {
            List<Type> allGenericInterfaces = getAllGenericInterfaces(handlerClass);
            List<Class> allGenericInterfacesClazz = allGenericInterfaces.stream()
                    .filter(t -> (t instanceof ParameterizedType))
                    .map(t -> (Class) ((ParameterizedType) t).getRawType())
                    .collect(Collectors.toList());
            if (allGenericInterfacesClazz.contains(EntryHandler.class)) {
                Type genericSuperclass = handlerClass.getGenericSuperclass();
                tableClass = (Class<T>) ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
                cache.putIfAbsent(handlerClass, tableClass);
                return tableClass;
            }

        }
        return tableClass;
    }

    /**
     * 获取该类实现的所有接口（带泛型）
     *
     * @param clazz 类
     * @return 泛型接口列表
     */
    private static List<Type> getAllGenericInterfaces(Class<?> clazz) {
        if (clazz == null) {
            return null;
        }
        LinkedHashSet<Type> interfacesFound = new LinkedHashSet<>();
        getAllGenericInterfaces(clazz, interfacesFound);
        return new ArrayList<>(interfacesFound);
    }

    private static void getAllGenericInterfaces(Class<?> clazz, LinkedHashSet<Type> interfacesFound) {
        while (clazz != null) {
            Type[] genericInterfaces = clazz.getGenericInterfaces();
            LinkedHashSet<Type> typesFound = new LinkedHashSet<>(Arrays.asList(genericInterfaces));

            Class<?>[] interfaces = clazz.getInterfaces();
            int length = interfaces.length;

            for (int i$ = 0; i$ < length; ++i$) {
                Class<?> i = interfaces[i$];
                Type type = genericInterfaces[i$];
                if (interfacesFound.add(type)) {
                    getAllGenericInterfaces(i, typesFound);
                }
            }

            clazz = clazz.getSuperclass();
        }
    }

}
